﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.ComponentModel;
using System.Windows.Data;
using System.Collections.Specialized;
using System.Diagnostics;

namespace CollectionViewSourceSpikes.Controls {

    [TemplatePart(Name = GroupsHostPanelName, Type = typeof(Panel))]
    [TemplatePart(Name = ItemsHostPanelName, Type = typeof(Panel))]
    [StyleTypedProperty(Property = "GroupStyle", StyleTargetType = typeof(GroupingViewGroup))]
    [StyleTypedProperty(Property = "ItemStyle", StyleTargetType = typeof(GroupingViewItem))]
    public class GroupingView : Control {
        
        private const string GroupsHostPanelName = "GroupsHost";
        private const string ItemsHostPanelName = "ItemsHost";
        private Panel groupsHost = null;
        private Panel itemsHost = null;
        
        private SourceHolder sourceHolder = null;
        private GroupingViewItem selectedItemControl = null;
        private ICollectionView collectionView = null;
        private object lastSelectedItemSource = null;

        public event RoutedPropertyChangedEventHandler<object> SelectedItemChanged;

        public GroupingView() {
            this.DefaultStyleKey = typeof(GroupingView);
            this.sourceHolder = new SourceHolder(this);
        }

        public Style GroupStyle {
            get { return (Style)GetValue(GroupStyleProperty); }
            set { SetValue(GroupStyleProperty, value); }
        }

        public static readonly DependencyProperty GroupStyleProperty =
            DependencyProperty.Register("GroupStyle", typeof(Style), typeof(GroupingView), new PropertyMetadata(OnGroupStyleChanged));

        private static void OnGroupStyleChanged(object sender, DependencyPropertyChangedEventArgs e) {

        }

        public DataTemplate GroupTemplate {
            get { return (DataTemplate)GetValue(GroupTemplateProperty); }
            set { SetValue(GroupTemplateProperty, value); }
        }

        public static readonly DependencyProperty GroupTemplateProperty =
            DependencyProperty.Register("GroupTemplate", typeof(DataTemplate), typeof(GroupingView), new PropertyMetadata(OnGroupTemplateChanged));

        private static void OnGroupTemplateChanged(object sender, DependencyPropertyChangedEventArgs e) {

        }

        public Style ItemStyle {
            get { return (Style)GetValue(ItemStyleProperty); }
            set { SetValue(ItemStyleProperty, value); }
        }

        public static readonly DependencyProperty ItemStyleProperty =
            DependencyProperty.Register("ItemStyle", typeof(Style), typeof(GroupingView), new PropertyMetadata(OnItemStyleChanged));

        private static void OnItemStyleChanged(object sender, DependencyPropertyChangedEventArgs e) {

        }

        public DataTemplate ItemTemplate {
            get { return (DataTemplate)GetValue(ItemTemplateProperty); }
            set { SetValue(ItemTemplateProperty, value); }
        }

        public static readonly DependencyProperty ItemTemplateProperty =
            DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(GroupingView), new PropertyMetadata(OnItemTemplateChanged));

        private static void OnItemTemplateChanged(object sender, DependencyPropertyChangedEventArgs e) {

        }

        public IEnumerable ItemsSource {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(GroupingView), new PropertyMetadata(OnItemsSourceChanged));

        private static void OnItemsSourceChanged(object sender, DependencyPropertyChangedEventArgs e) {
            GroupingView self = sender as GroupingView;
            if (self == null) {
                return;
            }
            if (e.NewValue == null) {
                self.UpdateCollectionView(null);
                return;
            }
            if (e.NewValue is ICollectionView) {
                self.UpdateCollectionView((ICollectionView)e.NewValue);
                return;
            }
            if (e.NewValue is ICollectionViewFactory) {
                self.UpdateCollectionView(((ICollectionViewFactory)e.NewValue).CreateView());
                return;
            }
            CollectionViewSource viewSource = new CollectionViewSource();
            viewSource.Source = e.NewValue;
            self.UpdateCollectionView(viewSource.View);
        }

        public object SelectedItem {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(GroupingView), new PropertyMetadata(OnSelectedItemChanged));

        private static void OnSelectedItemChanged(object sender, DependencyPropertyChangedEventArgs e) {
            ((GroupingView)sender).OnSelectedItemChanged(e.NewValue, e.OldValue);
        }

        private void OnSelectedItemChanged(object newValue, object oldValue) {
            GroupingViewItem item = this.sourceHolder.ItemViewFromSource(newValue);
            if (item == null && selectedItemControl != null) {
                selectedItemControl.IsSelected = false;
            } else if (item != null && !item.IsSelected) {
                item.IsSelected = true;
            }
            if (this.SelectedItemChanged != null) {
                this.SelectedItemChanged(this, new RoutedPropertyChangedEventArgs<object>(newValue, oldValue));
            }
        }

        private void UpdateCollectionView(ICollectionView newCollectionView) {
            if (collectionView != null) {
                collectionView.CollectionChanged -= OnCollectionViewCollectionChanged;
                collectionView.CurrentChanging -= OnCollectionViewCurrentChanging;
                collectionView.CurrentChanged -= OnCollectionViewCurrentChanged;
            }
            collectionView = newCollectionView;
            if (collectionView != null) {
                collectionView.CollectionChanged += OnCollectionViewCollectionChanged;
                collectionView.CurrentChanging += OnCollectionViewCurrentChanging;
                collectionView.CurrentChanged += OnCollectionViewCurrentChanged;
            }
            InitializeElement();
        }

        private void OnCollectionViewCurrentChanging(object sender, CurrentChangingEventArgs e) {

        }

        private void OnCollectionViewCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
            InitializeElement();
        }

        private void OnCollectionViewCurrentChanged(object sender, EventArgs e) {

        }

        public override void OnApplyTemplate() {
            base.OnApplyTemplate();

            this.groupsHost = this.GetTemplateChild(GroupsHostPanelName) as Panel;
            this.itemsHost = this.GetTemplateChild(ItemsHostPanelName) as Panel;
            InitializeElement();
        }

        private void InitializeElement() {
            if (this.groupsHost == null && this.itemsHost == null) {
                return;
            }
            if (this.groupsHost != null) {
                this.groupsHost.Children.Clear();
            }
            if (this.itemsHost != null) {
                this.itemsHost.Children.Clear();
            }
            this.sourceHolder.Clear();

            lastSelectedItemSource = selectedItemControl == null ? null : selectedItemControl.Source;
            selectedItemControl = null;
            AttachElements();

            CheckLastSelectedItemSource();
        }

        private bool isDispalyGroups = false;

        private void AttachElements() {
            if (this.collectionView == null) {
                return;
            }
            if (this.collectionView.Groups == null && this.itemsHost != null) {
                isDispalyGroups = false;
                foreach (object source in this.collectionView) {
                    GroupingViewItem item = CreateItem(source,false);
                    this.itemsHost.Children.Add(item);
                }
            } else if(this.collectionView.Groups != null && this.groupsHost != null) {
                isDispalyGroups = true;
                foreach (CollectionViewGroup groupView in this.collectionView.Groups) {
                    GroupingViewGroup group = CreateGroup(groupView);
                    this.groupsHost.Children.Add(group);
                }
            }
            UpdateState();
        }

        internal GroupingViewItem CreateItem(object source,bool reuse) {
            GroupingViewItem item = null;

            if (source is GroupingViewItem) {
                item = (GroupingViewItem)source;
            } else {
                item = new GroupingViewItem(this);
                item.Source = source;
            }

            if (this.ItemStyle != null) {
                item.Style = this.ItemStyle;
            }
            if (this.ItemTemplate != null) {
                item.ContentTemplate = this.ItemTemplate;
            } else {
                if (source != null) {
                    item.ContentTemplate = this.FindImplicitDataTemplate(source.GetType());
                }
            }

            this.sourceHolder.AddItem(item, source);
            
            return item;
        }

        internal GroupingViewGroup CreateGroup(CollectionViewGroup viewGroup) {
            GroupingViewGroup group = new GroupingViewGroup(this, viewGroup);
            group.Source = viewGroup.Name;
            if (this.GroupStyle != null) {
                group.Style = this.GroupStyle;
            }
            if (this.GroupTemplate != null) {
                group.GroupTemplate = this.GroupTemplate;
            } else {
                if(viewGroup.Name != null){
                    group.GroupTemplate = this.FindImplicitDataTemplate(viewGroup.Name.GetType());
                }
            }

            this.sourceHolder.AddGroup(group, group.Source);

            return group;
        }

        private void UpdateState() {
            VisualStateManager.GoToState(this, isDispalyGroups ? "DisplayGroups" : "DisplayItems", false);
        }

        internal void OnItemKeyDown(GroupingViewItem item, KeyEventArgs e) {

            GroupingViewItem targetItem = null;
            if (e.Key == Key.Up) {
                targetItem = this.sourceHolder.FindPreviousItem(item);
            } else if (e.Key == Key.Down) {
                targetItem = this.sourceHolder.FindNextItem(item);
            } else if (e.Key == Key.Space) {

            }
            if (targetItem != null && !targetItem.IsSelected) {
                targetItem.IsSelected = true;
            }
            if (targetItem != null && FocusManager.GetFocusedElement() != targetItem) {
                targetItem.Focus();
            }
        }

        internal bool PrepareItemSelection(GroupingViewItem item) {
            if (selectedItemControl != null) {
                if (selectedItemControl == item) {
                    return false;
                }
            }

            selectedItemControl = item;
            if (item != null) {
                item.IsSelected = true;
            }
            
            return true;
        }

        internal void UpdateSelectedItem(GroupingViewItem item) {
            if (item == selectedItemControl) {
                return;
            }
            GroupingViewItem oldSelectedItemControl = selectedItemControl;
            selectedItemControl = item;
            if (oldSelectedItemControl != null) {
                oldSelectedItemControl.IsSelected = false;
            }
            if (item != null) {
                item.Focus();
            }
            this.SelectedItem = item.Source;
        }

        internal void CheckLastSelectedItemSource() {
            GroupingViewItem item = this.sourceHolder.ItemViewFromSource(lastSelectedItemSource);
            if (item != null) {
                this.PrepareItemSelection(item);
            }
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) {
            GroupingViewItem firstItem = this.sourceHolder.FirstItem();
            if (firstItem != null) {
                firstItem.Focus();
            }
            
            e.Handled = true;
        }
    }
}
